package com.ceac.kronos.ejb.sessionbeans;

import com.ceac.kronos.ejb.entitybeans.Category;
import com.ceac.kronos.ejb.entitybeans.Checkpoint;
import com.ceac.kronos.ejb.entitybeans.Competition;
import com.ceac.kronos.ejb.entitybeans.Edition;
import com.ceac.kronos.ejb.entitybeans.Participant;
import com.ceac.kronos.ejb.entitybeans.Person;
import com.ceac.kronos.ejb.entitybeans.Race;
import com.ceac.kronos.ejb.entitybeans.Role;
import com.ceac.kronos.ejb.entitybeans.Time;

import java.math.BigDecimal;

import java.sql.Timestamp;

import java.util.Date;
import java.util.List;

import java.util.Vector;

import javax.ejb.Local;
import javax.ejb.Remote;
import javax.ejb.Stateless;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceContext;

@Stateless(name="KronosSessionEJB",
    mappedName = "CEAC-Kronos-KronosSessionEJB")
public class KronosSessionEJBBean implements KronosSessionEJB,
                                             KronosSessionEJBLocal {
    @PersistenceContext(unitName="Kronos")
    private EntityManager em;

    public KronosSessionEJBBean() {
    }

    public Time persistTime(Time time) {
        em.persist(time);
        return time;
    }

    public Time mergeTime(Time time) {
        return em.merge(time);
    }

    public void removeTime(Time time) {
        time = em.find(Time.class, time.getTimeId());
        em.remove(time);
    }

    /** <code>select o from Time o</code> */
    public List<Time> getTimeFindAll() {
        return em.createNamedQuery("Time.findAll").getResultList();
    }

    public Edition persistEdition(Edition edition) {
        em.persist(edition);
        return edition;
    }

    public Edition mergeEdition(Edition edition) {
        return em.merge(edition);
    }

    public void removeEdition(Edition edition) {
        edition = em.find(Edition.class, edition.getEditionId());
        em.remove(edition);
    }

    /** <code>select o from Edition o</code> */
    public List<Edition> getEditionFindAll() {
        return em.createNamedQuery("Edition.findAll").getResultList();
    }

    public Competition persistCompetition(Competition competition) {
        em.persist(competition);
        return competition;
    }

    public Competition mergeCompetition(Competition competition) {
        return em.merge(competition);
    }

    public void removeCompetition(Competition competition) {
        competition = em.find(Competition.class, competition.getCompetitionId());
        em.remove(competition);
    }

    /** <code>select o from Competition o</code> */
    public List<Competition> getCompetitionFindAll() {
        return em.createNamedQuery("Competition.findAll").getResultList();
    }

    public Category persistCategory(Category category) {
        em.persist(category);
        return category;
    }

    public Category mergeCategory(Category category) {
        return em.merge(category);
    }

    public void removeCategory(Category category) {
        category = em.find(Category.class, category.getCategoryId());
        em.remove(category);
    }

    /** <code>select o from Category o</code> */
    public List<Category> getCategoryFindAll() {
        return em.createNamedQuery("Category.findAll").getResultList();
    }

    public Participant persistParticipant(Participant participant) {
        em.persist(participant);
        return participant;
    }

    public Participant mergeParticipant(Participant participant) {
        return em.merge(participant);
    }

    public void removeParticipant(Participant participant) {
        participant = em.find(Participant.class, participant.getParticipantId());
        em.remove(participant);
    }

    /** <code>select o from Participant o</code> */
    public List<Participant> getParticipantFindAll() {
        return em.createNamedQuery("Participant.findAll").getResultList();
    }

    public Race persistRace(Race race) {
        em.persist(race);
        return race;
    }

    public Race mergeRace(Race race) {
        return em.merge(race);
    }

    public void removeRace(Race race) {
        race = em.find(Race.class, race.getRaceId());
        em.remove(race);
    }

    /** <code>select o from Race o</code> */
    public List<Race> getRaceFindAll() {
        return em.createNamedQuery("Race.findAll").getResultList();
    }

    public Checkpoint persistCheckpoint(Checkpoint checkpoint) {
        em.persist(checkpoint);
        return checkpoint;
    }

    public Checkpoint mergeCheckpoint(Checkpoint checkpoint) {
        return em.merge(checkpoint);
    }

    public void removeCheckpoint(Checkpoint checkpoint) {
        checkpoint = em.find(Checkpoint.class, checkpoint.getCheckpointId());
        em.remove(checkpoint);
    }

    /** <code>select o from Checkpoint o</code> */
    public List<Checkpoint> getCheckpointFindAll() {
        return em.createNamedQuery("Checkpoint.findAll").getResultList();
    }

    public Person persistPerson(Person person) {
        em.persist(person);
        return person;
    }

    public Person mergePerson(Person person) {
        return em.merge(person);
    }

    public void removePerson(Person person) {
        person = em.find(Person.class, person.getPersonId());
        em.remove(person);
    }

    /** <code>select o from Person o</code> */
    public List<Person> getPersonFindAll() {
        return em.createNamedQuery("Person.findAll").getResultList();
    }

    public Role persistRole(Role role) {
        em.persist(role);
        return role;
    }

    public Role mergeRole(Role role) {
        return em.merge(role);
    }

    public void removeRole(Role role) {
        role = em.find(Role.class, role.getRoleId());
        em.remove(role);
    }

    /** <code>select o from Role o</code> */
    public List<Role> getRoleFindAll() {
        return em.createNamedQuery("Role.findAll").getResultList();
    }

    /** <code>select o from Competition o where o.name=:name</code> */
    public Competition getCompetitionFindByName(String name) {
        return (Competition) em.createNamedQuery("Competition.findByName").setParameter("name", name).getSingleResult();
    }

    /** <code>select o from Person o where o.idCard = :idCard</code> */
    public Person getPersonFindByIdCard(String idCard) {
        return (Person) em.createNamedQuery("Person.findByIdCard").setParameter("idCard", idCard).getSingleResult();
    }

    /** <code>select o from Race o where o.name=:name</code> */
    public Race getRaceFindByName(Edition edition, String name) {
        return (Race) em.createNamedQuery("Race.findByName").setParameter("edition", edition).setParameter("name", name).getSingleResult();
    }

  /** <code>select o from Race o where o.name=:name</code> */
  public Edition getEditionFindByName(Competition competition, Integer editionNumber) {
      return (Edition) em.createNamedQuery("Edition.findByNumber").setParameter("competition", competition).setParameter("editionNumber", editionNumber).getSingleResult();
  }

    /** <code>select o from Edition o where o.competition=:competition</code> */
    public List<Edition> getEditionFindByCompetition(Competition competition) {
        return em.createNamedQuery("Edition.findByCompetition").setParameter("competition", competition).getResultList();
    }

    /** <code>select o from Edition o where o.competition=:competition and o.editionNumber=:editionNumber</code> */
    public List<Edition> getEditionFindByNumber(Competition competition,
                                                Integer editionNumber) {
        return em.createNamedQuery("Edition.findByNumber").setParameter("competition", competition).setParameter("editionNumber", editionNumber).getResultList();
    }

    /** <code>select o from Competition o where o.competitionId=:competitionId</code> */
    public List<Competition> getCompetitionFindById(Integer competitionId) {
        return em.createNamedQuery("Competition.findById").setParameter("competitionId", competitionId).getResultList();
    }

    /** <code>select o from Participant o where o.person = :person and o.race=:race</code> */
    public List<Participant> getParticipantFindParticipation(Person person,
                                                             Race race) {
        return em.createNamedQuery("Participant.findParticipation").setParameter("person", person).setParameter("race", race).getResultList();
    }
    
    /*
     * Create a new competition. If code exists, adds a new edition.
     * The new entity will have a single race of given distance to be run on the specified date
     * It will have a single checkpoint located at the end
     * 
     */
    public void createCompetition(String code, String name, String description, int edition, Date date, String raceName, int distance, int bibsStart, int bibsLimit) {
        List<Competition> competitions = em.createNamedQuery("Competition.findByCode").setParameter("code", code).getResultList();
        Competition competition = null;
        if(competitions==null || competitions.size()==0) {
            competition = new Competition();
            competition.setCode(code);
            competition.setName(name);
            competition.setDescription(description);
        }
        else {
          competition = competitions.get(0);
        }
        
        System.out.println(competition);
        
        List<Edition> editions = competition.getEditions();
        Edition currentEdition = null;
        boolean found = false;
        System.out.println(editions);
        System.out.println(editions.size());
        for(int i=0;i<editions.size() && !found;i++) {
            if(editions.get(i).getEditionNumber()==edition) {
              currentEdition = editions.get(i);
              found = true;
            }
        }
        if(!found) {
            currentEdition = new Edition();
            currentEdition.setEditionNumber(edition);
            currentEdition.setEventDate(new Timestamp(date.getTime()));
            currentEdition.setCompetition(competition);
            competition.getEditions().add(currentEdition);
        }

      System.out.println(currentEdition);
      
      Race race = new Race();
      race.setEdition(currentEdition);
      currentEdition.getRaces().add(race);
      race.setName(raceName);
      race.setDistance(new Float(distance));
      race.setLaps(1f);
      race.setReuseBibsNumbers(false);
      race.setStartTime(new Timestamp(date.getTime()));
      race.setStartBibsNumber(bibsStart);
      race.setEndBibsNumber(bibsLimit);

      System.out.println(race);
      
      
      Competition persistentCompetition = persistCompetition(competition);
      System.out.println(persistentCompetition);
    }
    
  public void closeEdition(BigDecimal editionId) {
    Timestamp now = new Timestamp((new Date()).getTime());
    List<Edition> editions = em.createNamedQuery("Edition.getEditionById").setParameter("editionId", editionId).getResultList();
    Edition edition = null;
    if(editions!=null && editions.size()==1) {
      edition = editions.get(0);
    }
    edition.setCloseDate(now);
  }
  
  public void openEdition(BigDecimal editionId) {
    //Automatically close all other editions that might still be open
      Timestamp now = new Timestamp((new Date()).getTime());
      List<Edition> editions = em.createNamedQuery("Edition.getEditionById").setParameter("editionId", editionId).getResultList();
      Edition edition = null;
      if(editions!=null && editions.size()==1) {
        edition = editions.get(0);
      }
      Competition competition = edition.getCompetition();
      if(competition!=null) {
        competition.getEditions();
        for(Edition e : competition.getEditions()) {            
            if(e.getCloseDate()==null || e.getCloseDate().after(now)) {
                e.setCloseDate(now);
            }
        }
      }
      edition.setOpenDate(now);
  }

  public void addCategories(String code, int editionNumber, Vector<BigDecimal> categoriesCollection) {
    Competition c = this.getCompetitionFindByCode(code).get(0);
    Edition e = this.getEditionFindByNumber(c, editionNumber).get(0);
    
  }
  
  public void startRace(BigDecimal raceId)  {
      
  }
  
  public void endRace(BigDecimal raceId) {
    
  }
  
  /**
     * Sets now as finish time for a given participant
     * 
     * @param raceId
     * @param bibsNumber
     */
  public void registerTime(int raceId, int bibsNumber) {
      Race race = this.getRaceFindById(raceId).get(0);
      Participant p = this.getParticipantFindByBibsNumber(race, bibsNumber).get(0);
      p.setFinishTime(new Timestamp((new Date()).getTime()));
      em.persist(p);
  }
  
    /**
     * Add a new participant to the unique open edition for the competition <code>competitionCode</code>
     * @param competitionCode
     */
    public void addParticipant(String competitionCode, String name, Date birthDate, String address, String city, String postalCode, String countryCode, String clubCode, String clubName) {
            
    }

    /** <code>select o from Competition o where o.name=:name</code> */
    public List<Competition> getCompetitionFindByCode(String name) {
        return em.createNamedQuery("Competition.findByCode").setParameter("name", name).getResultList();
    }

    /** <code>select o from Race o where o.raceId=:raceId</code> */
    public List<Race> getRaceFindById(Integer raceId) {
        return em.createNamedQuery("Race.findById").setParameter("raceId", raceId).getResultList();
    }

    /** <code>select o from Participant o where o.race = :race and o.bibsNumber=:bibsNumber</code> */
    public List<Participant> getParticipantFindByBibsNumber(Race race,
                                                            Integer bibsNumber) {
        return em.createNamedQuery("Participant.findByBibsNumber").setParameter("race", race).setParameter("bibsNumber", bibsNumber).getResultList();
    }
}
